use crate::ast::*;
use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::values::BasicValueEnum;

impl<'ctx> LLVMBackend<'ctx> {


    /// Generate ok_or() method - converts Option to Result
    /// Some -> Ok, None -> Err
    pub(super) fn generate_option_ok_or(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Option.ok_or() expects 1 argument (error value), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Generate error value
        let err_value = self.generate_expression(&arguments[0])?;
        let err_type = match self.infer_expression_type(&arguments[0]) {
            Some(t) => t,
            _ => return Err(CompilerError::internal("Could not infer error type")),
        };

        let tag = self.extract_option_tag(option_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_some =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "ok_or_check")?;

        // Create blocks: some_block (create Ok), none_block (create Err)
        let some_block = self.context.append_basic_block(func, "ok_or_some");
        let none_block = self.context.append_basic_block(func, "ok_or_none");
        let continue_block = self.context.append_basic_block(func, "ok_or_continue");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: create Result::Ok
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;

        // Store some_value in a temporary variable to use in Result::Ok constructor
        let some_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let some_temp_alloca = self.builder.build_alloca(some_type, "ok_or_some_temp")?;
        self.builder.build_store(some_temp_alloca, some_value)?;

        let some_temp_var = format!("__ok_or_some_{}", location.line);
        let old_some_value = self
            .variable_map
            .insert(some_temp_var.clone(), some_temp_alloca.into());
        let old_some_type = self
            .variable_type_map
            .insert(some_temp_var.clone(), *option_type.some_type.clone());

        // Create Result::Ok(some_value) expression
        let some_var_expr = Expression::Variable(some_temp_var.clone());
        let ok_path_expr = Expression::Path(crate::ast::PathExpr {
            segments: vec!["Result".to_string(), "Ok".to_string()],
            location: *location,
        });
        let ok_call_expr = Expression::Call(CallExpr {
            callee: Box::new(ok_path_expr),
            arguments: vec![some_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let ok_result = self.generate_expression(&ok_call_expr)?;

        // Restore variable map
        if let Some(old) = old_some_value {
            self.variable_map.insert(some_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&some_temp_var);
        }
        if let Some(old) = old_some_type {
            self.variable_type_map.insert(some_temp_var, old);
        } else {
            self.variable_type_map.remove(&some_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // None block: create Result::Err
        self.builder.position_at_end(none_block);

        // Store err_value in a temporary variable to use in Result::Err constructor
        let err_llvm_type = self.nrc_type_to_llvm_type(&Some(err_type.clone()))?;
        let err_temp_alloca = self.builder.build_alloca(err_llvm_type, "ok_or_err_temp")?;
        self.builder.build_store(err_temp_alloca, err_value)?;

        let err_temp_var = format!("__ok_or_err_{}", location.line);
        let old_err_value = self
            .variable_map
            .insert(err_temp_var.clone(), err_temp_alloca.into());
        let old_err_type = self
            .variable_type_map
            .insert(err_temp_var.clone(), err_type.clone());

        // Create Result::Err(err_value) expression
        let err_var_expr = Expression::Variable(err_temp_var.clone());
        let err_path_expr = Expression::Path(crate::ast::PathExpr {
            segments: vec!["Result".to_string(), "Err".to_string()],
            location: *location,
        });
        let err_call_expr = Expression::Call(CallExpr {
            callee: Box::new(err_path_expr),
            arguments: vec![err_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let err_result = self.generate_expression(&err_call_expr)?;

        // Restore variable map
        if let Some(old) = old_err_value {
            self.variable_map.insert(err_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&err_temp_var);
        }
        if let Some(old) = old_err_type {
            self.variable_type_map.insert(err_temp_var, old);
        } else {
            self.variable_type_map.remove(&err_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either Ok or Err Result
        self.builder.position_at_end(continue_block);
        // Infer Result type from ok_result
        let result_type = match self.infer_expression_type(&ok_call_expr) {
            Some(Type::Result(rt)) => rt,
            _ => crate::ast::types::ResultType {
                ok_type: option_type.some_type.clone(),
                err_type: Box::new(err_type.clone()),
                location: *location,
            },
        };
        let result_llvm_type = self.get_result_llvm_type(&result_type)?;
        let phi = self.builder.build_phi(result_llvm_type, "ok_or_result")?;
        phi.add_incoming(&[(&ok_result, some_block), (&err_result, none_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate ok_or_else() method - converts Option to Result with computed error
    /// Some -> Ok, None -> Err (computed by function)
    pub(super) fn generate_option_ok_or_else(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Option.ok_or_else() expects 1 argument (function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_option_tag(option_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "ok_or_else_check",
        )?;

        // Create blocks: some_block (create Ok), none_block (call function, create Err)
        let some_block = self.context.append_basic_block(func, "ok_or_else_some");
        let none_block = self.context.append_basic_block(func, "ok_or_else_none");
        let continue_block = self.context.append_basic_block(func, "ok_or_else_continue");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: create Result::Ok
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;

        // Infer error type from function return type
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(arguments[0].clone()),
            arguments: vec![],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let err_type = match self.infer_expression_type(&call_expr) {
            Some(t) => t,
            _ => {
                return Err(CompilerError::internal(
                    "Could not infer error type from function",
                ))
            }
        };

        // Store some_value in a temporary variable to use in Result::Ok constructor
        let some_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let some_temp_alloca = self
            .builder
            .build_alloca(some_type, "ok_or_else_some_temp")?;
        self.builder.build_store(some_temp_alloca, some_value)?;

        let some_temp_var = format!("__ok_or_else_some_{}", location.line);
        let old_some_value = self
            .variable_map
            .insert(some_temp_var.clone(), some_temp_alloca.into());
        let old_some_type = self
            .variable_type_map
            .insert(some_temp_var.clone(), *option_type.some_type.clone());

        // Create Result::Ok(some_value) expression
        let some_var_expr = Expression::Variable(some_temp_var.clone());
        let ok_path_expr = Expression::Path(crate::ast::PathExpr {
            segments: vec!["Result".to_string(), "Ok".to_string()],
            location: *location,
        });
        let ok_call_expr = Expression::Call(CallExpr {
            callee: Box::new(ok_path_expr),
            arguments: vec![some_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let ok_result = self.generate_expression(&ok_call_expr)?;

        // Restore variable map
        if let Some(old) = old_some_value {
            self.variable_map.insert(some_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&some_temp_var);
        }
        if let Some(old) = old_some_type {
            self.variable_type_map.insert(some_temp_var, old);
        } else {
            self.variable_type_map.remove(&some_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // None block: call function to compute error, create Result::Err
        self.builder.position_at_end(none_block);
        let err_value = self.generate_expression(&call_expr)?;

        // Store err_value in a temporary variable to use in Result::Err constructor
        let err_llvm_type = self.nrc_type_to_llvm_type(&Some(err_type.clone()))?;
        let err_temp_alloca = self
            .builder
            .build_alloca(err_llvm_type, "ok_or_else_err_temp")?;
        self.builder.build_store(err_temp_alloca, err_value)?;

        let err_temp_var = format!("__ok_or_else_err_{}", location.line);
        let old_err_value = self
            .variable_map
            .insert(err_temp_var.clone(), err_temp_alloca.into());
        let old_err_type = self
            .variable_type_map
            .insert(err_temp_var.clone(), err_type.clone());

        // Create Result::Err(err_value) expression
        let err_var_expr = Expression::Variable(err_temp_var.clone());
        let err_path_expr = Expression::Path(crate::ast::PathExpr {
            segments: vec!["Result".to_string(), "Err".to_string()],
            location: *location,
        });
        let err_call_expr = Expression::Call(CallExpr {
            callee: Box::new(err_path_expr),
            arguments: vec![err_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let err_result = self.generate_expression(&err_call_expr)?;

        // Restore variable map
        if let Some(old) = old_err_value {
            self.variable_map.insert(err_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&err_temp_var);
        }
        if let Some(old) = old_err_type {
            self.variable_type_map.insert(err_temp_var, old);
        } else {
            self.variable_type_map.remove(&err_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either Ok or Err Result
        self.builder.position_at_end(continue_block);
        // Infer Result type from ok_result
        let result_type = match self.infer_expression_type(&ok_call_expr) {
            Some(Type::Result(rt)) => rt,
            _ => crate::ast::types::ResultType {
                ok_type: option_type.some_type.clone(),
                err_type: Box::new(err_type.clone()),
                location: *location,
            },
        };
        let result_llvm_type = self.get_result_llvm_type(&result_type)?;
        let phi = self
            .builder
            .build_phi(result_llvm_type, "ok_or_else_result")?;
        phi.add_incoming(&[(&ok_result, some_block), (&err_result, none_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate map_or() method - maps Some or returns default
    /// Some -> f(value), None -> default
    pub(super) fn generate_option_map_or(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 2 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Option.map_or() expects 2 arguments (default, function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Generate default value
        let default_value = self.generate_expression(&arguments[0])?;
        let default_type = match self.infer_expression_type(&arguments[0]) {
            Some(t) => t,
            _ => return Err(CompilerError::internal("Could not infer default type")),
        };

        let tag = self.extract_option_tag(option_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_some =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "map_or_check")?;

        // Create blocks: some_block (call function), none_block (return default)
        let some_block = self.context.append_basic_block(func, "map_or_some");
        let none_block = self.context.append_basic_block(func, "map_or_none");
        let continue_block = self.context.append_basic_block(func, "map_or_continue");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract value, call function
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;

        let some_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let some_temp_alloca = self.builder.build_alloca(some_type, "some_temp")?;
        self.builder.build_store(some_temp_alloca, some_value)?;

        let some_temp_var = format!("__map_or_some_{}", location.line);
        let old_some_value = self
            .variable_map
            .insert(some_temp_var.clone(), some_temp_alloca.into());
        let old_some_type = self
            .variable_type_map
            .insert(some_temp_var.clone(), *option_type.some_type.clone());

        // Call function
        let func_expr = arguments[1].clone();
        let some_var_expr = Expression::Variable(some_temp_var.clone());
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(func_expr),
            arguments: vec![some_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });

        let mapped_value = self.generate_expression(&call_expr)?;

        // Restore variable map
        if let Some(old) = old_some_value {
            self.variable_map.insert(some_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&some_temp_var);
        }
        if let Some(old) = old_some_type {
            self.variable_type_map.insert(some_temp_var, old);
        } else {
            self.variable_type_map.remove(&some_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // None block: return default
        self.builder.position_at_end(none_block);
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either mapped value or default
        self.builder.position_at_end(continue_block);
        let result_llvm_type = self.nrc_type_to_llvm_type(&Some(default_type.clone()))?;
        let phi = self.builder.build_phi(result_llvm_type, "map_or_result")?;
        phi.add_incoming(&[(&mapped_value, some_block), (&default_value, none_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate map_or_else() method - maps Some or computes default
    /// Some -> f(value), None -> default_fn()
    pub(super) fn generate_option_map_or_else(
        &mut self,
        option_value: BasicValueEnum<'ctx>,
        option_type: &crate::ast::types::OptionType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 2 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Option.map_or_else() expects 2 arguments (default_fn, function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_option_tag(option_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_some = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "map_or_else_check",
        )?;

        // Create blocks: some_block (call function), none_block (call default_fn)
        let some_block = self.context.append_basic_block(func, "map_or_else_some");
        let none_block = self.context.append_basic_block(func, "map_or_else_none");
        let continue_block = self
            .context
            .append_basic_block(func, "map_or_else_continue");

        self.builder
            .build_conditional_branch(is_some, some_block, none_block)?;

        // Some block: extract value, call function
        self.builder.position_at_end(some_block);
        let some_value = self.extract_option_some_value(option_value, option_type)?;

        let some_type = self.nrc_type_to_llvm_type(&Some(*option_type.some_type.clone()))?;
        let some_temp_alloca = self.builder.build_alloca(some_type, "some_temp")?;
        self.builder.build_store(some_temp_alloca, some_value)?;

        let some_temp_var = format!("__map_or_else_some_{}", location.line);
        let old_some_value = self
            .variable_map
            .insert(some_temp_var.clone(), some_temp_alloca.into());
        let old_some_type = self
            .variable_type_map
            .insert(some_temp_var.clone(), *option_type.some_type.clone());

        // Call function
        let func_expr = arguments[1].clone();
        let some_var_expr = Expression::Variable(some_temp_var.clone());
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(func_expr),
            arguments: vec![some_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });

        let mapped_value = self.generate_expression(&call_expr)?;

        // Restore variable map
        if let Some(old) = old_some_value {
            self.variable_map.insert(some_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&some_temp_var);
        }
        if let Some(old) = old_some_type {
            self.variable_type_map.insert(some_temp_var, old);
        } else {
            self.variable_type_map.remove(&some_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // None block: call default_fn
        self.builder.position_at_end(none_block);
        let default_call_expr = Expression::Call(CallExpr {
            callee: Box::new(arguments[0].clone()),
            arguments: vec![],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });
        let default_value = self.generate_expression(&default_call_expr)?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either mapped value or default
        self.builder.position_at_end(continue_block);
        // Infer result type from mapped value (both should have same type)
        let result_type = match self.infer_expression_type(&call_expr) {
            Some(t) => t,
            _ => match self.infer_expression_type(&default_call_expr) {
                Some(t) => t,
                _ => {
                    return Err(CompilerError::internal(
                        "Could not infer map_or_else result type",
                    ))
                }
            },
        };
        let result_llvm_type = self.nrc_type_to_llvm_type(&Some(result_type.clone()))?;
        let phi = self
            .builder
            .build_phi(result_llvm_type, "map_or_else_result")?;
        phi.add_incoming(&[(&mapped_value, some_block), (&default_value, none_block)]);
        Ok(phi.as_basic_value())
    }

}
